﻿using System;
using System.Text;
using CoreLibrary;
using CoreLibrary.Security;
using CoreLibrary.Service;
using Entities.DataBase.Persistent;

namespace WebService.Security {

    public class PassportUtil {

        private const char Delimiter = (char)0;
        private const int PassportLiftTime = 30;

        public static string WritePassport(User account) {
            var dateExpiredInTicks = DateTime.Now.AddDays(PassportLiftTime).Ticks;
            var builder = new StringBuilder();

            return GetCipher().EncryptToBase64(
                builder.Append(Util.GetString(account.UserId))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.Email))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.FirstName))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.LastName))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.Title))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.UserRole))
                       .Append(Delimiter)
                       .Append(Util.GetString(account.Enabled))
                       .Append(Delimiter)
                       .Append(Util.GetString(dateExpiredInTicks)).ToString());
        }


        public static UserToken ReadPassport(string passport) {

            var decrypted = GetCipher().DecryptFromBase64(passport);
            if (string.IsNullOrEmpty(decrypted)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid. Not recognised passpord");    
            }

            var output = decrypted.Split(Delimiter);
            if (output.Length != 8) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport length is not valid");    
            }

            var userId = output[0];
            if (string.IsNullOrEmpty(userId)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid. UserId is required.");    
            }

            var email = output[1];
            if (string.IsNullOrEmpty(email)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, Email is required");
            }

            var firstName = output[2];
            if (string.IsNullOrEmpty(firstName)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, FirstName is required.");
            }

            var lastName = output[3];
            if (string.IsNullOrEmpty(lastName)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, LastName is required.");
            }

            var title = output[4];
            if (string.IsNullOrEmpty(title)) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, Title is required.");
            }

            int role;
            var parsed = int.TryParse(output[5], out role);
            if (!parsed) {
                 throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, UserRole is required.");
            }

            bool enabled;
            var enabledParsed = bool.TryParse(output[6], out enabled);
            if (!enabledParsed) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, Enabled is required.");
            }

            long timeLeft;
            var timeLeftParsed = long.TryParse(output[7], out timeLeft);
            if (!timeLeftParsed) {
                 throw ServiceErrors.CreateUnauthenticatedException("Passport is not valid, ExpireDate is required.");
            }

            var dateExpired = new DateTime(timeLeft);
            if (DateTime.Now.CompareTo(dateExpired) >= 0) {
                throw ServiceErrors.CreateUnauthenticatedException("Passport is expired.");
            }

            return new UserToken {
                UserId    = userId,
                Email     = email,
                FirstName = firstName,
                LastName  = lastName,
                Title     = title,
                UserRole  = role,
                Enabled   = enabled,
            };
        }

        private static volatile Cryptos _cryptos;
        private readonly static object Locker = new object();

        private static Cryptos GetCipher() {
            if (_cryptos == null) {
                lock (Locker) {
                    if (_cryptos == null) {
                        _cryptos = Cryptos.CreateAes(PKey, PrKey);
                    }
                }
            }
            return _cryptos;
        }

        private readonly static byte[] PKey = new byte[] {
            25,     128,    217,   23,
            11,     12,     33,    55,
            66,     7,      18,    19,
            98,     19,     13,    15,
        };

        private readonly static byte[] PrKey = new byte[] {
            25,     128,    217,    23,
            11,     12,     33,     55,
            66,     7,      18,     19,
            98,     19,     13,     15,
        };

    }
}